﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class provides methods and structures to describe a mathematical octonion.
    /// </summary>
    [Serializable]
    public class Octonion : IOctonion
    {
        /// <summary>
        /// The real number part of the octonion.
        /// </summary>
        private double realPart;

        /// <summary>
        /// The i part of the octonion.
        /// </summary>
        private double i;

        /// <summary>
        /// The j part of the octonion.
        /// </summary>
        private double j;

        /// <summary>
        /// The k part of the octonion.
        /// </summary>
        private double k;

        /// <summary>
        /// The l part of the octonion.
        /// </summary>
        private double l;

        /// <summary>
        /// The il part of the octonion.
        /// </summary>
        private double il;

        /// <summary>
        /// The jl part of the octonion.
        /// </summary>
        private double jl;

        /// <summary>
        /// The kl part of the octonion.
        /// </summary>
        private double kl;

        /// <summary>
        /// Initializes a new instance of the <see cref="Octonion"/> class.
        /// </summary>
        public Octonion()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Octonion"/> class.
        /// </summary>
        /// <param name="realPart">The real number part of the octonion.</param>
        public Octonion(double realPart)
        {
            this.realPart = realPart;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Octonion"/> class.
        /// </summary>
        /// <param name="realPart">The real number part of the octonion.</param>
        /// <param name="i">The i part of the octonion.</param>
        /// <param name="j">The j part of the octonion.</param>
        /// <param name="k">The k part of the octonion.</param>
        /// <param name="l">The l part of the octonion.</param>
        /// <param name="il">The il part of the octonion.</param>
        /// <param name="jl">The jl part of the octonion.</param>
        /// <param name="kl">The kl part of the octonion.</param>
        public Octonion(double realPart, double i, double j, double k, double l, double il, double jl, double kl)
        {
            this.realPart = realPart;
            this.i = i;
            this.j = j;
            this.k = k;
            this.l = l;
            this.il = il;
            this.jl = jl;
            this.kl = kl;
        }

        /// <summary>
        /// Gets or sets the real number part of the octonion.
        /// </summary>
        /// <value>The real number part of the octonion.</value>
        public double RealPart
        {
            get { return realPart; }
            set { realPart = value; }
        }

        /// <summary>
        /// Gets or sets the i part of the octonion.
        /// </summary>
        /// <value>The i part of the octonion.</value>
        public double I
        {
            get { return i; }
            set { i = value; }
        }

        /// <summary>
        /// Gets or sets the j part of the octonion.
        /// </summary>
        /// <value>The j part of the octonion.</value>
        public double J
        {
            get { return j; }
            set { j = value; }
        }

        /// <summary>
        /// Gets or sets the k part of the octonion.
        /// </summary>
        /// <value>The k part of the octonion.</value>
        public double K
        {
            get { return k; }
            set { k = value; }
        }

        /// <summary>
        /// Gets or sets the l part of the octonion.
        /// </summary>
        /// <value>The l part of the octonion.</value>
        public double L
        {
            get { return l; }
            set { l = value; }
        }

        /// <summary>
        /// Gets or sets the il part of the octonion.
        /// </summary>
        /// <value>The il part of the octonion.</value>
        public double IL
        {
            get { return il; }
            set { il = value; }
        }

        /// <summary>
        /// Gets or sets the jl part of the octonion.
        /// </summary>
        /// <value>The jl part of the octonion.</value>
        public double JL
        {
            get { return jl; }
            set { jl = value; }
        }

        /// <summary>
        /// Gets or sets the kl part of the octonion.
        /// </summary>
        /// <value>The kl part of the octonion.</value>
        public double KL
        {
            get { return kl; }
            set { kl = value; }
        }

        /// <summary>
        /// Copies the current instance of an octonion.
        /// </summary>
        /// <returns>The copy of the current instance.</returns>
        public Octonion Copy()
        {
            return new Octonion(this.realPart, this.i, this.j, this.k, this.l,
                                this.il, this.jl, this.kl);
        }

        /// <summary>
        /// Compares the current instance of an octonion to another instance.
        /// </summary>
        /// <param name="compare">The octonion to compare.</param>
        /// <returns>True if both octonions are even otherwise, false.</returns>
        public bool CompareTo(Octonion compare)
        {
            return this == compare;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first octonion.</param>
        /// <param name="b">The second octonion.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Octonion a, Octonion b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return ((a.RealPart == b.RealPart) && (a.I == b.I) && (a.J == b.J) && (a.K == b.K)
                    && (a.L == b.L) && (a.IL == b.IL) && (a.JL == b.JL) && (a.KL == b.KL));
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first octonion.</param>
        /// <param name="b">The second octonion.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Octonion a, Octonion b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first octonion.</param>
        /// <param name="b">The second octonion.</param>
        /// <returns>The result of the operator.</returns>
        public static Octonion operator +(Octonion a, Octonion b)
        {
            if (a == (Octonion) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Octonion) null)
            {
                throw new ArgumentNullException("b");
            }

            Octonion result = new Octonion();

            result.RealPart = a.RealPart + b.RealPart;
            result.I = a.I + b.I;
            result.J = a.J + b.J;
            result.K = a.K + b.K;
            result.L = a.L + b.L;
            result.IL = a.IL + b.IL;
            result.JL = a.JL + b.JL;
            result.KL = a.KL + b.KL;

            return result;
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first octonion.</param>
        /// <param name="b">The second octonion.</param>
        /// <returns>The result of the operator.</returns>
        public static Octonion operator -(Octonion a, Octonion b)
        {
            if (a == (Octonion) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Octonion) null)
            {
                throw new ArgumentNullException("b");
            }

            Octonion result = new Octonion();

            result.RealPart = a.RealPart - b.RealPart;
            result.I = a.I - b.I;
            result.J = a.J - b.J;
            result.K = a.K - b.K;
            result.L = a.L - b.L;
            result.IL = a.IL - b.IL;
            result.JL = a.JL - b.JL;
            result.KL = a.KL - b.KL;

            return result;
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The octonion for the multiplication.</param>
        /// <param name="value">The value to multiply with.</param>
        /// <returns>The result of the operator.</returns>
        public static Octonion operator *(Octonion a, double value)
        {
            if (a == (Octonion) null)
            {
                throw new ArgumentNullException("a");
            }

            Octonion result = new Octonion();

            result.RealPart = a.RealPart * value;
            result.I = a.I * value;
            result.J = a.J * value;
            result.K = a.K * value;
            result.L = a.L * value;
            result.IL = a.IL * value;
            result.JL = a.JL * value;
            result.KL = a.KL * value;

            return result;
        }

        /// <summary>
        /// Powers the current sedenion raised to the specified power.
        /// </summary>
        /// <param name="value">The value to raised with.</param>
        /// <returns>The new sedenion raised to the specified power.</returns>
        public Octonion Pow(double value)
        {
            Octonion result = new Octonion();

            result.RealPart = Math.Pow(this.realPart, value);
            result.I = Math.Pow(this.i, value);
            result.J = Math.Pow(this.j, value);
            result.K = Math.Pow(this.k, value);
            result.L = Math.Pow(this.l, value);
            result.IL = Math.Pow(this.il, value);
            result.JL = Math.Pow(this.jl, value);
            result.KL = Math.Pow(this.kl, value);

            return result;
        }

        /// <summary>
        /// Formats a number for the ToString method.
        /// </summary>
        /// <param name="number">The number to format.</param>
        /// <returns>The formated number.</returns>
        private static String FormatNumber(double number)
        {
            if (number < 0)
            {
                return " - " + (-number).ToString();
            }

            return " + " + number.ToString();
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            String result = this.realPart.ToString();

            result += FormatNumber(this.i) + "i";
            result += FormatNumber(this.j) + "j";
            result += FormatNumber(this.k) + "k";
            result += FormatNumber(this.l) + "l";
            result += FormatNumber(this.il) + "il";
            result += FormatNumber(this.jl) + "jl";
            result += FormatNumber(this.kl) + "kl";

            return result;
        }
    }
}